const loader = require("./loaders-chain/inline-loader1");


//把一个loader从一个绝对路径变成一个loader对象
function createLoaderObject(loader){
  let normal = require(loader)
  let pitch = normal.pitch;
  let raw = normal.raw;//决定loader的参数是字符串还是Buffer
  return {
      path:loader,//存放此loader的绝对路径
      normal,
      pitch,
      raw,
      data:{},//每个loader第一可以携带一个自定义data对象
      pitchExecuted:false,//此loader的pitch函数是够已经执行过
      normalExecuted:false,//此loader的normal函数是否已经执行过
  }
}

function convertArgs(args,raw){
  if(raw && !Buffer.isBuffer(args[0])){
    args[0] = Buffer.isBuffer(args[0])
  }else if(!raw && Buffer.isBuffer(args[0])){
    args[0] = args[0].toString('utf8')
  }
}

function iterateNormalLoaders(processOptions,loaderContext,args,pitchingCallback){
  if(loaderContext.loaderIndex<0){
    return pitchingCallback(null,...args)
  }
  let currentLoader = loaderContext.loaders[loaderContext.loaderIndex];
  if(currentLoader.normalExecuted){
    loaderContext.loaderIndex--
    return iterateNormalLoaders(processOptions,loaderContext,args,pitchingCallback)
  }
  let fn = currentLoader.normal;
  currentLoader.normalExecuted = true;
  convertArgs(args,currentLoader.raw);
  runSyncOrAsync(fn,loaderContext,args,(err, ...returArgs)=>{
    if(err) return pitchingCallback(err)
    return iterateNormalLoaders(processOptions,loaderContext,returArgs,pitchingCallback);
  })
}

//处理资源
function processResource(processOptions, loaderContext, pitchingCallback) {
  //文件的绝对路径
  processOptions.readResource(loaderContext.resource, (err, resouceBuffer) => {
    processOptions.resouceBuffer = resouceBuffer;
    loaderContext.loaderIndex--; //定位到最后有一个loader
    iterateNormalLoaders(
      processOptions,
      loaderContext,
      [resouceBuffer],
      pitchingCallback
    );
  });
}

function iteratePitchingLoaders(
  processOptions,
  loaderContext,
  pitchingCallback
) {
  //说明所有的Loader的pitch都已经执行完成
  if (loaderContext.loaderIndex >= loaderContext.loaders.length) {
    return processResource(processOptions, loaderContext, pitchingCallback);
  }
  let currentLoader = loaderContext.loaders[loaderContext.loaderIndex];
  if (currentLoader.pitchExecuted) {
    loaderContext.loaderIndex++; //如果当前的pitch已经执行过了就可以让当前的索引加1
    return iteratePitchingLoaders( processOptions,loaderContext,pitchingCallback)
  }
  let fn = currentLoader.pitch;
  currentLoader.pitchExecuted = true; //表示当前的loader的pitch已经处理过
  //没有pitch继续执行
  if (!fn) {
    return iteratePitchingLoaders(
      processOptions,
      loaderContext,
      pitchingCallback
    );
  }
  //以同步或者异步方式执行fn
  runSyncOrAsync(
    fn,
    loaderContext,
    [
      loaderContext.remainingRequest,
      loaderContext.previousRequest,
      loaderContext.data,
    ],
    (err, ...args) => {
      //如果有返回值,索引减少1，并执行前一个loader的normal
      if (args.length > 0 && args.some((item) => item)) {
        loaderContext.loaderIndex--;//索引减少1
        interateNormalLoaders(processOptions,loaderContext,pitchingCallback)
      } else {
        return iteratePitchingLoaders(
          processOptions,
          loaderContext,
          pitchingCallback
        );
      }
    }
  );
}

//同步或者异步方式调用方法
function runSyncOrAsync(fn, loaderContext, args, runCallback) {
  let isAsync = true; //这个是表示符：用来标志fn的执行是同步还是异步，默认同步
  loaderContext.callback = (...args) => {
    runCallback(null, ...args);
  };
  loaderContext.async = () => {
    isAsync = false; //从同步改为异步
    return loaderContext.callback;
  };
  //在执行pitch方法的时候，this指向loaderContext
  let result = fn.apply(loaderContext, args);
  if (isAsync) {//如果是同步的执行的话，会立刻向下执行以下个loader
      return runCallback(null,result)
  }
  //如果是异步的话，那就什么都不要做
}

//defineProperties 因为每次索引不一样所以取的值都不一样 动态取值
function runLoaders(options, finalCallback) {
  let {
    resource,
    loaders = [],
    context = {},
    readResource = fs.readFile,
  } = options;
  let loaderObjects = loaders.map(createLoaderObject);
  let loaderContext = context;
  loaderContext.resource = resource; //需加載的资源
  loaderContext.readResource = readResource; //读取资源的方法
  loaderContext.loaders = loaderObjects; //所有的loader对象
  loaderContext.loaderIndex = 0; //当前正在执行的Loader索引
  loaderContext.callback = null; //回调
  loaderContext.async = null; //把loader的执行从同步变成异步
  //所有loader加上resource
  Object.defineProperty(loaderContext, 'request', {
    get() {
      //loader1!loader2!loader!index.js
      return loaderContext.loaders
        .map((loader) => loader.path)
        .concat(loaderContext.resource)
        .join('!');
    },
  });
  //从当前loader下一个开始一直到结束，加上要加载的资源
  Object.defineProperty(loaderContext, 'remainingRequest', {
    get() {
      return loaderContext.loaders
        .slice(loaderContext.loaderIndex + 1)
        .map((loader) => loader.path)
        .concat(loaderContext.resource)
        .join('!');
    },
  });
  //从当前loader开始一直到结束，加上要加载的资源
  Object.defineProperty(loaderContext, 'currentRequest', {
    get() {
      return loaderContext.loaders
        .slice(loaderContext.loaderIndex)
        .map((loader) => loader.path)
        .concat(loaderContext.resource)
        .join('!');
    },
  });
   //从第一个到当前loader的前一个
   Object.defineProperty(loaderContext, 'previousRequest', {
    get() {
      return loaderContext.loaders
        .slice(0, loaderContext.loaderIndex)
        .map((loader) => loader.path)
        .join('!');
    },
  });
  //每个loader都有自己的data
  Object.defineProperty(loaderContext, 'data', {
    get() {
      return loaderContext.loaders[loaderContext.loaderIndex].data;
    },
  });
  let processOptions = {
    resouceBuffer: null, //将要存放读到的原始文件 index.js内容的Buffer
    readResource,
  };
  iteratePitchingLoaders(processOptions, loaderContext, (err, result) => {
    finalCallback(err, {
      result,
      resourceBuffer: processOptions.resouceBuffer,
    });
  });
}

exports.runLoaders = runLoaders;
